#!/bin/bash

# 部署脚本 - 自动化 Docker 服务部署

# 严格模式：错误处理和未定义变量检查
set -euo pipefail

# 颜色定义
readonly GREEN='\033[0;32m'
readonly RED='\033[0;31m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # 无颜色

# 日志函数
log_info() {
    echo -e "${GREEN}[INFO]${NC} $*"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $*" >&2
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $*"
}

# 检查依赖
check_dependencies() {
    local dependencies=("docker" "envsubst" "jq")
    for cmd in "${dependencies[@]}"; do
        if ! command -v "$cmd" &> /dev/null; then
            log_error "未找到命令：$cmd"
            exit 1
        fi
    done
}

# 获取本地 IP 地址
get_local_ip() {
    local ip
    if [[ "$OSTYPE" == "darwin"* ]]; then
        ip=$(ipconfig getifaddr en0)
    else
        ip=$(ip addr show | grep -E 'inet .*global' | awk '{print $2}' | cut -d/ -f1 | head -n1)
    fi
    
    if [ -z "$ip" ]; then
        log_error "无法获取本地 IP 地址"
        exit 1
    fi
    
    echo "$ip"
}

# 验证环境变量格式
validate_env_value() {
    local var_name=$1
    local var_value=$2
    
    case $var_name in
        "WEB_UI_PORT"|"CALLBACK_PORT"|"BASE_API_PORT"|"DOWNLOAD_PORT")
            if ! [[ $var_value =~ ^[0-9]+$ ]] || [ $var_value -lt 1024 ] || [ $var_value -gt 65535 ]; then
                log_error "环境变量 $var_name 必须是有效的端口号(1024-65535)"
                return 1
            fi
            ;;
        "GROUP_CHAT_PREFIX"|"GROUP_CHAT_KEYWORD")
            if [ -z "$var_value" ]; then
                log_warning "环境变量 $var_name 为空，可能影响机器人的触发"
            fi
            ;;
    esac
    return 0
}

# 加载并验证环境变量
load_env_variables() {
    local env_file="./.env"
    
    if [ ! -f "$env_file" ]; then
        log_error "未找到 .env 文件"
        exit 1
    fi
    
    # shellcheck disable=SC1090
    source "$env_file"
    
    # 检查关键环境变量
    local required_vars=(
        "SOURCE_CODE_URL"
        "DOW_CONTAINER_NAME"
        "DIFY_API_KEY"
        "WEB_UI_PORT"
        "CALLBACK_PORT"
        "BASE_API_PORT"
        "DOWNLOAD_PORT"
        "GROUP_CHAT_PREFIX"
        "GROUP_CHAT_KEYWORD"
    )
    
    for var in "${required_vars[@]}"; do
        if [ -z "${!var:-}" ]; then
            log_error "环境变量 $var 未设置"
            exit 1
        fi
        
        # 验证环境变量值的格式
        if ! validate_env_value "$var" "${!var}"; then
            exit 1
        fi
    done
}

# 替换环境变量
replace_env_variables() {
    local template_file="$1"
    local output_file="$2"
    
    if [ ! -f "$template_file" ]; then
        log_error "模板文件 $template_file 不存在"
        return 1
    fi
    
    # 使用 envsubst 替换变量
    envsubst < "$template_file" > "$output_file"
    
    if [ ! -s "$output_file" ]; then
        log_error "环境变量替换失败"
        return 1
    fi
    
    log_info "环境变量替换成功：$output_file"
}

# Docker 镜像登录
docker_login() {
    if [[ -n "${DOCKER_USERNAME:-}" && -n "${DOCKER_PASSWORD:-}" && -n "${DOCKER_REGISTRY:-}" ]]; then
        log_info "登录 Docker 镜像仓库：${DOCKER_REGISTRY}"
        if ! echo "$DOCKER_PASSWORD" | docker login "$DOCKER_REGISTRY" -u "$DOCKER_USERNAME" --password-stdin; then
            log_error "Docker 登录失败"
            exit 1
        fi
    else
        log_warning "未提供完整的 Docker 仓库凭据，跳过登录"
    fi
}

# 部署 gewechat 服务
deploy_gewechat() {
    local container_name="${GEWECHAT_CONTAINER_NAME:-gewe}"
    local data_dir="./data/gewechat/$container_name"
    
    mkdir -p "$data_dir"
    
    docker pull registry.cn-chengdu.aliyuncs.com/tu1h/wechotd:alpine
    docker tag registry.cn-chengdu.aliyuncs.com/tu1h/wechotd:alpine gewe
    
    if ! docker ps --format '{{.Names}}' | grep -w "$container_name"; then
        docker run -itd \
            -v "$data_dir:/root/temp" \
            -p "${BASE_API_PORT:-2531}:2531" \
            -p "${DOWNLOAD_PORT:-2532}:2532" \
            --restart=always \
            --name="$container_name" \
            gewe
        
        log_info "gewechat 服务已启动"
    else
        log_info "gewechat 服务已在运行"
    fi
}

# 生成config.json
generate_config() {
    local config_template="template/config-template.json"
    local config_output="target/config.json"
    local local_ip
    
    mkdir -p "$(dirname "$config_output")"
    local_ip=$(get_local_ip)

    # 将GROUP_CHAT_PREFIX和GROUP_CHAT_KEYWORD转换为数组
    IFS=',' read -r -a prefix_array <<< "${GROUP_CHAT_PREFIX:-}"
    IFS=',' read -r -a keyword_array <<< "${GROUP_CHAT_KEYWORD:-}"

    # 构建jq过滤器
    prefix_filter="$(printf '%s\n' "${prefix_array[@]}" | jq -R . | jq -s .)"
    keyword_filter="$(printf '%s\n' "${keyword_array[@]}" | jq -R . | jq -s .)"

    # 使用 jq 处理 JSON 模板
    jq --arg dify_api_key "$DIFY_API_KEY" \
       --arg base_url "http://$local_ip:${BASE_API_PORT}/v2/api" \
       --arg callback_url "http://$local_ip:${CALLBACK_PORT}/v2/api/callback/collect" \
       --arg download_url "http://$local_ip:${DOWNLOAD_PORT}/download" \
       --argjson prefix "$prefix_filter" \
       --argjson keyword "$keyword_filter" \
       '.dify_api_key = $dify_api_key | 
        .gewechat_base_url = $base_url | 
        .gewechat_callback_url = $callback_url | 
        .gewechat_download_url = $download_url |
        .group_chat_prefix = $prefix |
        .group_chat_keyword = $keyword' \
       "$config_template" > "$config_output"

    log_info "已生成 $config_output"
}

# 生成docker-compose.yml
generate_docker_compose() {
    local template="template/docker-compose-template.yml"
    local output="target/docker-compose.yml"
    
    mkdir -p "$(dirname "$output")"
    
    # 确保环境变量已被导出
    source ./.env
    export SOURCE_CODE_URL
    export DOW_CONTAINER_NAME
    export WEB_UI_PORT
    export CALLBACK_PORT
    export BASE_API_PORT
    export DOWNLOAD_PORT
    export DOCKER_IMAGE_URL
    
    # 使用 envsubst 替换模板中的环境变量
    envsubst < "$template" > "$output"
    
    log_info "已生成 $output"
}

# 添加部署前的健康检查
pre_deploy_checks() {
    log_info "开始部署前检查..."
    
    # 检查磁盘空间
    local disk_usage
    disk_usage=$(df -h . | awk 'NR==2 {print $5}' | sed 's/%//')
    if [ "${disk_usage:-0}" -gt 90 ]; then
        log_error "磁盘空间不足，使用率: ${disk_usage}%"
        exit 1
    fi

    # 检查必要的目录权限
    local required_dirs=("docker" "plugins" "backups")
    for dir in "${required_dirs[@]}"; do
        if [ -d "$dir" ] && ! [ -w "$dir" ]; then
            log_error "目录 $dir 缺少写入权限"
            exit 1
        fi
    done

    # 检查 Docker 服务状态
    if ! docker info &> /dev/null; then
        log_error "Docker 服务未运行或无法访问"
        exit 1
    fi
}

# 复制插件目录
copy_plugins() {
    local template="template/plugins"
    local output="target/plugins"

    mkdir -p "$output"

    # 检查源目录是否存在
    if [ ! -d "$template" ]; then
        log_error "插件模板目录不存在：$template"
        return 1
    fi
    
    # 如果目标目录存在，先备份
    if [ -d "$output" ]; then
        local timestamp=$(date +"%Y%m%d_%H%M%S")
        local backup_dir="data/backups/plugins_${timestamp}"
        log_info "备份现有插件目录到：$backup_dir"
        mv "$output" "$backup_dir"
    fi
    
    # 创建目标目录并复制文件
    mkdir -p "$output"
    cp -r "$template/"* "$output/"
    
    if [ $? -eq 0 ]; then
        log_info "插件目录复制成功"
    else
        log_error "插件目录复制失败"
        return 1
    fi
}

# 主部署流程
main() {
    # 检查部署环境
    pre_deploy_checks
    check_dependencies
    docker_login
    rm -rf target
    
    # 加载并验证环境变量
    load_env_variables
    
    # 确保SOURCE_CODE_URL和DOW_CONTAINER_NAME已设置
    if [[ -z "${SOURCE_CODE_URL:-}" || -z "${DOW_CONTAINER_NAME:-}" ]]; then
        log_error "SOURCE_CODE_URL或DOW_CONTAINER_NAME未设置"
        exit 1
    fi

    # 部署项目
    deploy_gewechat
    generate_config
    copy_plugins
    generate_docker_compose
    
    # 重新加载环境变量，确保变量值可用
    source ./.env
    
    # 在target目录克隆代码，不然使用docker compose部署，服务会报错"NameError: name 'logger' is not defined"
    log_info "克隆源代码到target目录..."
    if ! git clone "${SOURCE_CODE_URL}" "./target/${DOW_CONTAINER_NAME}"; then
        log_error "源代码克隆失败"
        exit 1
    fi

    # 将生成的config.json移动到克隆的代码目录中
    log_info "移动配置文件到代码目录..."
    mv "./target/config.json" "./target/${DOW_CONTAINER_NAME}/config.json"

    # 将生成的docker-compose.yml移动到克隆的代码目录中
    log_info "移动docker-compose文件到代码目录..."
    mv "./target/docker-compose.yml" "./target/${DOW_CONTAINER_NAME}/docker/docker-compose.yml"

    # 将plugins目录下的文件移动到克隆的代码目录中
    log_info "移动插件文件到代码目录..."
    mkdir -p "./target/${DOW_CONTAINER_NAME}/plugins"
    mv "./target/plugins/"* "./target/${DOW_CONTAINER_NAME}/plugins/"
    
    # 执行 Docker Compose 部署
    log_info "开始部署服务..."
    if ! docker compose -f ./target/${DOW_CONTAINER_NAME}/docker/docker-compose.yml up -d; then
        log_error "部署失败，正在回滚..."
        docker compose -f ./target/${DOW_CONTAINER_NAME}/docker/docker-compose.yml down
        exit 1
    fi

    # 验证服务是否正常启动
    sleep 3
    if ! docker ps --format '{{.Names}}' | grep -q "^${DOW_CONTAINER_NAME}$"; then
        log_error "服务启动验证失败，容器未正常运行，正在回滚..."
        docker compose -f target/${DOW_CONTAINER_NAME}/docker/docker-compose.yml down
        exit 1
    fi

    sleep 3
    docker logs -f -n 100 ${DOW_CONTAINER_NAME}
    log_info "部署完成！服务已成功启动"
}

# 执行主流程
main